
// Created by 21126 on 2023/5/19.
//
#include "U2_S5.h"

unsigned char MS523_ADDRESS_S5[4] = {0x28, 0x29, 0x2A, 0x2B};

i2c_addr_def u2_s5_init(int fd)
{
    i2c_addr_def result;
    unsigned char real_slave_address;

    result.flag = 0;

    if (i2c_detect(fd, MS523_ADDRESS_S5, sizeof(MS523_ADDRESS_S5), &real_slave_address) >= 0)
    {
        result.fd = fd;
        result.addr = real_slave_address;
        result.flag = 1;

        u2_ms253_init(result.fd, result.addr);
    }

    return result;
}

void u2_ms253_init(int fd, unsigned char slave_address)
{
    // ms253软复位
    i2c_write_byte(fd, slave_address, CommandReg, RESET_COMMAND);
    //i2c_delay(2000);

    i2c_write_byte(fd, slave_address, ModeReg, 0x3D);

    // 计数器重载值0x30
    i2c_write_byte(fd, slave_address, TReloadRegH, 0x00);
    i2c_write_byte(fd, slave_address, TReloadRegL, 0x30);

    // 计数器受TModeReg寄存器影响，非门控模式，计数器清零后置ComIrqReg寄存器的TimerIRq为1，prescaler为0xD3E
    i2c_write_byte(fd, slave_address, TModeReg, 0x8D);
    i2c_write_byte(fd, slave_address, TPrescalerReg, 0x3E);

    // 对发送进行100%ASK的调制
    i2c_write_byte(fd, slave_address, TxAutoReg, 0x40);

    // 禁止串行UART中MX和DTRQ
    u2_clear_bit_mask(fd, slave_address, TestPinEnReg, 0x80);

    i2c_write_byte(fd, slave_address, TxAutoReg, 0x40);

    u2_clear_bit_mask(fd, slave_address, TxControlReg, 0x03);

    //delay_1ms(500);

    u2_set_bit_mask(fd, slave_address, TxControlReg, 0x03);

    // 打开crypto1单元
    i2c_write_byte(fd, slave_address, Status2Reg, 0x08);

    i2c_write_byte(fd, slave_address, ModeReg, 0x3D);

    i2c_write_byte(fd, slave_address, RxSelReg, 0x86);

    // 接收器信号电压增益系数：48dB
    i2c_write_byte(fd, slave_address, RFCfgReg, 0x7F);

    i2c_write_byte(fd, slave_address, TReloadRegL, 30);
    i2c_write_byte(fd, slave_address, TReloadRegH, 0);

    i2c_write_byte(fd, slave_address, TModeReg, 0x8D);
    i2c_write_byte(fd, slave_address, TPrescalerReg, 0x3E);

    //delay_1ms(500);

    u2_set_bit_mask(fd, slave_address, TxControlReg, 0x03);
}

void u2_clear_bit_mask(int fd, unsigned char slave_address, unsigned char reg_address, unsigned char mask)
{
    unsigned char tmp = 0x00;

    i2c_read_bytes(fd, slave_address, reg_address, &tmp, 1);

    i2c_write_byte(fd, slave_address, reg_address, tmp & ~mask);
}

void u2_set_bit_mask(int fd, unsigned char slave_address, unsigned char reg_address, unsigned char mask)
{
    unsigned char tmp = 0x00;

    i2c_read_bytes(fd, slave_address, reg_address, &tmp, 1);

    i2c_write_byte(fd, slave_address, reg_address, tmp | mask);
}

unsigned char u2_pcd_request(int fd, unsigned char slave_address, unsigned char req_code, unsigned char *tag_type)
{
    unsigned char status;
    __u16  unLen;
    unsigned char ucComMS523Buf[MAXRLEN];

    u2_clear_bit_mask(fd, slave_address, Status2Reg, 0x08);
    i2c_write_byte(fd, slave_address, BitFramingReg, 0x07);
    u2_set_bit_mask(fd, slave_address, TxControlReg, 0x03);

    ucComMS523Buf[0] = req_code;

    status = u2_pcd_commands_523(fd, slave_address, 0x0C, ucComMS523Buf, 1, ucComMS523Buf, &unLen);
    if ((status == MI_OK) && (unLen == 0x10))
    {
        *tag_type = ucComMS523Buf[0];
        *(tag_type + 1) = ucComMS523Buf[1];
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

unsigned char u2_pcd_anti_collision(int fd, unsigned char slave_address, unsigned char *snr)
{
    unsigned char status;
    unsigned char i,snr_check=0;
    __u16  unLen;
    unsigned char ucComMS523Buf[MAXRLEN];


    u2_clear_bit_mask(fd, slave_address, Status2Reg, 0x08);
    i2c_write_byte(fd, slave_address, BitFramingReg, 0x00);
    u2_clear_bit_mask(fd, slave_address, CollReg, 0x80);

    ucComMS523Buf[0] = 0x93;
    ucComMS523Buf[1] = 0x20;

    status = u2_pcd_commands_523(fd, slave_address, 0x0C, ucComMS523Buf, 2, ucComMS523Buf, &unLen);

    if (status == MI_OK)
    {
        for (i = 0; i < 4; i++)
        {
            *(snr + i)  = ucComMS523Buf[i];
            snr_check ^= ucComMS523Buf[i];
        }
        if (snr_check != ucComMS523Buf[i])
        {
            status = MI_ERR;
        }
    }

    u2_set_bit_mask(fd, slave_address, CollReg, 0x80);
    return status;
}

unsigned char u2_pcd_select(int fd, unsigned char slave_address, unsigned char *snr)
{
    unsigned char status;
    unsigned char i;
    __u16 unLen;
    unsigned char ucComMS523Buf[MAXRLEN];

    ucComMS523Buf[0] = 0x93;
    ucComMS523Buf[1] = 0x70;
    ucComMS523Buf[6] = 0;
    for (i = 0; i < 4; i++)
    {
        ucComMS523Buf[i + 2] = *(snr + i);
        ucComMS523Buf[6]  ^= *(snr + i);
    }
    u2_calculate_CRC(fd, slave_address, ucComMS523Buf, 7, &ucComMS523Buf[7]);

    u2_clear_bit_mask(fd, slave_address, Status2Reg, 0x08);

    status = u2_pcd_commands_523(fd, slave_address, 0x0C,ucComMS523Buf,9,ucComMS523Buf,&unLen);

    if ((status == MI_OK) && (unLen == 0x18))
    {
        status = MI_OK;
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

unsigned char u2_pcd_auth_state(int fd, unsigned char slave_address, unsigned char mode, unsigned char len, unsigned char *key, unsigned char *snr)
{
    unsigned char status;
    __u16  unLen;
    unsigned char i, ucComMS523Buf[MAXRLEN];
    unsigned char tmp;

    ucComMS523Buf[0] = mode;
    ucComMS523Buf[1] = len;
    for (i = 0; i < 6; i++)
    {
        ucComMS523Buf[i + 2] = *(key + i);
    }
    for (i = 0; i < 4; i++)
    {
        ucComMS523Buf[i + 8] = *(snr + i);
    }


    status = u2_pcd_commands_523(fd, slave_address, 0x0E, ucComMS523Buf, 12, ucComMS523Buf, &unLen);
    i2c_read_bytes(fd, slave_address, Status2Reg, &tmp, 1);
    if ((status != MI_OK) || (!(tmp & 0x08)))
    {
        status = MI_ERR;
    }

    return status;
}

unsigned char u2_pcd_read(int fd, unsigned char slave_address, unsigned char len, unsigned char *data)
{
    unsigned char status;
    __u16  unLen;
    unsigned char i,ucComMS523Buf[MAXRLEN];

    ucComMS523Buf[0] = 0x30;
    ucComMS523Buf[1] = len;
    u2_calculate_CRC(fd, slave_address, ucComMS523Buf, 2, &ucComMS523Buf[2]);

    status = u2_pcd_commands_523(fd, slave_address, 0x0C, ucComMS523Buf, 4, ucComMS523Buf, &unLen);
    if ((status == MI_OK) && (unLen == 0x90))
    //   {   memcpy(pData, ucComMS523Buf, 16);   }
    {
        for (i = 0; i < 16; i++)
        {
          *(data + i) = ucComMS523Buf[i];
        }
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

unsigned char u2_pcd_write(int fd, unsigned char slave_address, unsigned char len, unsigned char *data)
{
    unsigned char status;
    __u16  unLen;
    unsigned char i, ucComMS523Buf[MAXRLEN];

    ucComMS523Buf[0] = 0xA0;
    ucComMS523Buf[1] = len;
    u2_calculate_CRC(fd, slave_address, ucComMS523Buf, 2, &ucComMS523Buf[2]);

    status = u2_pcd_commands_523(fd, slave_address,0x0C, ucComMS523Buf, 4, ucComMS523Buf, &unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMS523Buf[0] & 0x0F) != 0x0A))
    {
    status = MI_ERR;
    }

    if (status == MI_OK)
    {
        //memcpy(ucComMS523Buf, pData, 16);
        for (i = 0; i < 16; i++)
        {
            ucComMS523Buf[i] = *(data + i);
        }
        u2_calculate_CRC(fd, slave_address, ucComMS523Buf, 16, &ucComMS523Buf[16]);

        status = u2_pcd_commands_523(fd, slave_address, 0x0C, ucComMS523Buf, 18, ucComMS523Buf, &unLen);
        if ((status != MI_OK) || (unLen != 4) || ((ucComMS523Buf[0] & 0x0F) != 0x0A))
        {
            status = MI_ERR;
        }
    }

    return status;
}

unsigned char u2_pcd_halt(int fd, unsigned char address)
{
    __u16  unLen;
    unsigned char ucComMS523Buf[MAXRLEN];

    ucComMS523Buf[0] = 0x50;
    ucComMS523Buf[1] = 0;
    u2_calculate_CRC(fd, address, ucComMS523Buf, 2, &ucComMS523Buf[2]);

    u2_pcd_commands_523(fd, address, 0x0C, ucComMS523Buf, 4, ucComMS523Buf, &unLen);

    return MI_OK;
}

unsigned char u2_pcd_commands_523(int fd, unsigned char i2c_addr, unsigned char Command, unsigned char *pInData, unsigned char InLenByte, unsigned char *pOutData, __u16  *pOutLenBit)
{
    unsigned char status = MI_ERR;
    unsigned char irqEn   = 0x00;
    unsigned char waitFor = 0x00;
    unsigned char lastBits;
    unsigned char n;
    __u16 i;

    switch (Command)
    {
        case 0x0E:
            irqEn = 0x12;
            waitFor = 0x10;
            break;
        case 0x0C:
            irqEn = 0x77;
            waitFor = 0x30;
            break;
        default:
            break;
    }

    i2c_write_byte(fd, i2c_addr, ComIEnReg, irqEn | 0x80);
    u2_clear_bit_mask(fd, i2c_addr, ComIrqReg, 0x80);
    i2c_write_byte(fd, i2c_addr, CommandReg, 0x00);
    u2_set_bit_mask(fd, i2c_addr, FIFOLevelReg, 0x80);

    //TODO:maybe error
    i2c_write_bytes(fd, i2c_addr, FIFODataReg, pInData, InLenByte);

    i2c_write_byte(fd,i2c_addr,CommandReg, Command);

    if (Command == 0x0C)
    {
        u2_set_bit_mask(fd, i2c_addr, BitFramingReg, 0x80);
    }

    i = 800;
    do
    {
        i2c_read_bytes(fd, i2c_addr, ComIrqReg, &n, 1);
        i--;
    }
    while ((i != 0) && !(n & 0x01) && !(n & waitFor));
    u2_clear_bit_mask(fd, i2c_addr, BitFramingReg,  0x80);

    if (i != 0)
    {
        i2c_read_bytes(fd, i2c_addr, ErrorReg, &n, 1);
        if (!(n & 0x1B))
        {
            status = MI_OK;
            if (n & irqEn & 0x01)
            {
                status = MI_NOTAGERR;
            }
            if (Command == 0x0C)
            {
                i2c_read_bytes(fd, i2c_addr, FIFOLevelReg, &n, 1);
                i2c_read_bytes(fd, i2c_addr, ControlReg, &lastBits, 1);
                lastBits &= 0x07;
                if (lastBits)
                {
                  *pOutLenBit = (n - 1) * 8 + lastBits;
                }
                else
                {
                  *pOutLenBit = n * 8;
                }
                if (n == 0)
                {
                    n = 1;
                }
                if (n > MAXRLEN)
                {
                    n = MAXRLEN;
                }
                i2c_read_bytes(fd, i2c_addr, FIFODataReg, pOutData, n);
            }
        }
        else
        {
            status = MI_ERR;
        }
    }
    u2_set_bit_mask(fd, i2c_addr, ControlReg, 0x80);           // stop timer now
    i2c_write_byte(fd, i2c_addr, CommandReg, 0x00);
    return status;
}

void u2_calculate_CRC(int fd, unsigned char address, unsigned char *in_data, unsigned char len, unsigned char *out_data)
{
    unsigned char i,n;

    u2_clear_bit_mask(fd, address, DivIrqReg, 0x04);
    i2c_write_byte(fd, address, CommandReg, 0x00);
    u2_set_bit_mask(fd, address, FIFOLevelReg, 0x80);

    //TODO:maybe error
    i2c_read_bytes(fd, address,FIFODataReg, in_data, len);
    i2c_write_byte(fd, address, CommandReg, 0x03);
    i = 0xFF;
    do
    {
        i2c_read_bytes(fd, address, DivIrqReg, &n, 1);
        i--;
    }
    while ((i != 0) && !(n & 0x04));
    i2c_read_bytes(fd, address, CRCResultRegL, &out_data[0], 1);
    i2c_read_bytes(fd, address, CRCResultRegM, &out_data[1], 1);
}

unsigned char u2_s5_detect(int fd, unsigned char slave_address, unsigned char *card_num)
{
    unsigned char type[2];

    if(u2_pcd_request(fd, slave_address, 0x52, type) == MI_OK)
    {
        if(u2_pcd_anti_collision(fd, slave_address, card_num) == MI_OK)
        {
            if(u2_pcd_select(fd, slave_address, card_num) == MI_OK)
            {
                return 1;
            }
        }
    }

    return 0;
}

unsigned char u2_s5_verify(int fd, unsigned char slave_address, unsigned char auth_mode, unsigned char len, unsigned char *Key, unsigned char *snr)
{
    unsigned char rt = 2;
    if (len <= 63)
    {
        rt = u2_pcd_auth_state(fd, slave_address, auth_mode, len, Key, snr);
    }
    return rt;
}

unsigned char u2_s5_write_data(int fd, unsigned char slave_address, unsigned char len, unsigned char *data)
{
    unsigned char rt = 2;
    if(len <= 63)
    {
        rt = u2_pcd_write(fd, slave_address, len, data);
    }

    return rt;
}

unsigned char u2_s5_read_data(int fd, unsigned char slave_address, unsigned char len, unsigned char *data)
{
    unsigned char rt = 2;
    if(len <= 63)
    {
        rt = u2_pcd_read(fd, slave_address, len, data);
    }

    return rt;
}

void u2_s5_sleep(int fd, unsigned char slave_address)
{
    u2_pcd_halt(fd, slave_address);
}